package cn.edu.scau.cmi.ema.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;


import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.Session;
import cn.edu.scau.cmi.ema.dao.*;
import cn.edu.scau.cmi.ema.domain.*;
import cn.edu.scau.cmi.ema.util.CmiSqlUtil;
import cn.edu.scau.cmi.ema.domain.base.CmiPagination;
import cn.edu.scau.cmi.ema.service.base.CmiEmaService;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import javax.persistence.Query;
import java.util.Iterator;
@Service("AdditiveService")
@Transactional

public class AdditiveServiceImpl extends CmiEmaService implements AdditiveService {
	public AdditiveServiceImpl() {
	}

	@Transactional
	public Additive saveAdditive(Additive additive) {
		Set<Additiveaddress> existingAdditiveaddresssForAdditive = new HashSet<Additiveaddress>();
		Set<Foodadditive> existingFoodadditivesForAdditive = new HashSet<Foodadditive>();
		Set<Foodbatchadditive> existingFoodbatchadditivesForAdditive = new HashSet<Foodbatchadditive>();
		Set<Purchaseadditive> existingPurchaseadditivesForAdditive = new HashSet<Purchaseadditive>();

		Set<Additiveaddress> selectedAdditiveaddresssForAdditive = additive.getAdditiveaddresssForAdditive();
		Set<Foodadditive> selectedFoodadditivesForAdditive = additive.getFoodadditivesForAdditive();
		Set<Foodbatchadditive> selectedFoodbatchadditivesForAdditive = additive.getFoodbatchadditivesForAdditive();
		Set<Purchaseadditive> selectedPurchaseadditivesForAdditive = additive.getPurchaseadditivesForAdditive();

		if (additive.getId() == null) {

			Additive existingAdditive = additiveDAO.findAdditiveByPhysicalPrimaryKey(additive.getName());

			if (isEquals(additive, existingAdditive)) {
				return existingAdditive;
			}

			additive = additiveDAO.store(additive);
			additiveDAO.flush();

			for (Additiveaddress relatedAdditiveaddress : selectedAdditiveaddresssForAdditive) {
				relatedAdditiveaddress.setAdditive(additive);
				additiveaddressDAO.store(relatedAdditiveaddress);
			}
			for (Foodadditive relatedFoodadditive : selectedFoodadditivesForAdditive) {
				relatedFoodadditive.setAdditive(additive);
				foodadditiveDAO.store(relatedFoodadditive);
			}
			for (Foodbatchadditive relatedFoodbatchadditive : selectedFoodbatchadditivesForAdditive) {
				relatedFoodbatchadditive.setAdditive(additive);
				foodbatchadditiveDAO.store(relatedFoodbatchadditive);
			}
			for (Purchaseadditive relatedPurchaseadditive : selectedPurchaseadditivesForAdditive) {
				relatedPurchaseadditive.setAdditive(additive);
				purchaseadditiveDAO.store(relatedPurchaseadditive);
			}
		} else {
			Additive existingAdditive = additiveDAO.findAdditiveByPrimaryKey(additive.getId());

			existingAdditiveaddresssForAdditive = existingAdditive.getAdditiveaddresssForAdditive();
			existingFoodadditivesForAdditive = existingAdditive.getFoodadditivesForAdditive();
			existingFoodbatchadditivesForAdditive = existingAdditive.getFoodbatchadditivesForAdditive();
			existingPurchaseadditivesForAdditive = existingAdditive.getPurchaseadditivesForAdditive();

			Set<Additiveaddress> prepareDeleteAdditiveaddresssForAdditive = new HashSet<Additiveaddress>();
			Set<Additiveaddress> prepareAddAdditiveaddresssForAdditive = new HashSet<Additiveaddress>();

			prepareDeleteAdditiveaddresssForAdditive.addAll(selectedAdditiveaddresssForAdditive);
			prepareDeleteAdditiveaddresssForAdditive.addAll(existingAdditiveaddresssForAdditive);

			prepareAddAdditiveaddresssForAdditive.addAll(selectedAdditiveaddresssForAdditive);
			prepareAddAdditiveaddresssForAdditive.addAll(existingAdditiveaddresssForAdditive);

			prepareDeleteAdditiveaddresssForAdditive.removeAll(selectedAdditiveaddresssForAdditive);
			prepareAddAdditiveaddresssForAdditive.removeAll(existingAdditiveaddresssForAdditive);

			for (Additiveaddress relatedAdditiveaddress : prepareAddAdditiveaddresssForAdditive) {
				relatedAdditiveaddress.setAdditive(additive);
				additiveaddressDAO.store(relatedAdditiveaddress);
			}

			for (Additiveaddress relatedAdditiveaddress : prepareDeleteAdditiveaddresssForAdditive) {
				relatedAdditiveaddress.setAdditive(null);
				additiveaddressDAO.store(relatedAdditiveaddress);
			}
			Set<Foodadditive> prepareDeleteFoodadditivesForAdditive = new HashSet<Foodadditive>();
			Set<Foodadditive> prepareAddFoodadditivesForAdditive = new HashSet<Foodadditive>();

			prepareDeleteFoodadditivesForAdditive.addAll(selectedFoodadditivesForAdditive);
			prepareDeleteFoodadditivesForAdditive.addAll(existingFoodadditivesForAdditive);

			prepareAddFoodadditivesForAdditive.addAll(selectedFoodadditivesForAdditive);
			prepareAddFoodadditivesForAdditive.addAll(existingFoodadditivesForAdditive);

			prepareDeleteFoodadditivesForAdditive.removeAll(selectedFoodadditivesForAdditive);
			prepareAddFoodadditivesForAdditive.removeAll(existingFoodadditivesForAdditive);

			for (Foodadditive relatedFoodadditive : prepareAddFoodadditivesForAdditive) {
				relatedFoodadditive.setAdditive(additive);
				foodadditiveDAO.store(relatedFoodadditive);
			}

			for (Foodadditive relatedFoodadditive : prepareDeleteFoodadditivesForAdditive) {
				relatedFoodadditive.setAdditive(null);
				foodadditiveDAO.store(relatedFoodadditive);
			}
			Set<Foodbatchadditive> prepareDeleteFoodbatchadditivesForAdditive = new HashSet<Foodbatchadditive>();
			Set<Foodbatchadditive> prepareAddFoodbatchadditivesForAdditive = new HashSet<Foodbatchadditive>();

			prepareDeleteFoodbatchadditivesForAdditive.addAll(selectedFoodbatchadditivesForAdditive);
			prepareDeleteFoodbatchadditivesForAdditive.addAll(existingFoodbatchadditivesForAdditive);

			prepareAddFoodbatchadditivesForAdditive.addAll(selectedFoodbatchadditivesForAdditive);
			prepareAddFoodbatchadditivesForAdditive.addAll(existingFoodbatchadditivesForAdditive);

			prepareDeleteFoodbatchadditivesForAdditive.removeAll(selectedFoodbatchadditivesForAdditive);
			prepareAddFoodbatchadditivesForAdditive.removeAll(existingFoodbatchadditivesForAdditive);

			for (Foodbatchadditive relatedFoodbatchadditive : prepareAddFoodbatchadditivesForAdditive) {
				relatedFoodbatchadditive.setAdditive(additive);
				foodbatchadditiveDAO.store(relatedFoodbatchadditive);
			}

			for (Foodbatchadditive relatedFoodbatchadditive : prepareDeleteFoodbatchadditivesForAdditive) {
				relatedFoodbatchadditive.setAdditive(null);
				foodbatchadditiveDAO.store(relatedFoodbatchadditive);
			}
			Set<Purchaseadditive> prepareDeletePurchaseadditivesForAdditive = new HashSet<Purchaseadditive>();
			Set<Purchaseadditive> prepareAddPurchaseadditivesForAdditive = new HashSet<Purchaseadditive>();

			prepareDeletePurchaseadditivesForAdditive.addAll(selectedPurchaseadditivesForAdditive);
			prepareDeletePurchaseadditivesForAdditive.addAll(existingPurchaseadditivesForAdditive);

			prepareAddPurchaseadditivesForAdditive.addAll(selectedPurchaseadditivesForAdditive);
			prepareAddPurchaseadditivesForAdditive.addAll(existingPurchaseadditivesForAdditive);

			prepareDeletePurchaseadditivesForAdditive.removeAll(selectedPurchaseadditivesForAdditive);
			prepareAddPurchaseadditivesForAdditive.removeAll(existingPurchaseadditivesForAdditive);

			for (Purchaseadditive relatedPurchaseadditive : prepareAddPurchaseadditivesForAdditive) {
				relatedPurchaseadditive.setAdditive(additive);
				purchaseadditiveDAO.store(relatedPurchaseadditive);
			}

			for (Purchaseadditive relatedPurchaseadditive : prepareDeletePurchaseadditivesForAdditive) {
				relatedPurchaseadditive.setAdditive(null);
				purchaseadditiveDAO.store(relatedPurchaseadditive);
			}
			additive = additiveDAO.store(additive);
			additiveDAO.flush();
		}

		additiveaddressDAO.flush();
		foodadditiveDAO.flush();
		foodbatchadditiveDAO.flush();
		purchaseadditiveDAO.flush();
		return additive;
	}

	public void deleteAdditive(Additive additive) {
		additiveDAO.remove(additive);
		additiveDAO.flush();
	}

	@Transactional
	public void deleteAdditives(Set<Integer> deleteAdditivesId) {
		Query query = additiveDAO.createNamedQuery("deleteAdditivesById", -1, -1);
		query.setParameter(1, deleteAdditivesId);
		query.executeUpdate();
	}

	@Transactional
	public Set<Additive> loadAdditives() {
		return additiveDAO.findAllAdditives();

	}

	@Transactional
	public Set<Additive> loadReferenceAdditives(Set<Additive> additives) {
		Iterator<Additive> additivesIterator = additives.iterator();
		Set<Additive> referencedAdditives = new HashSet<>();
		while (additivesIterator.hasNext()) {
			Additive currentAdditive = additivesIterator.next();

			if (!currentAdditive.getAdditiveaddresssForAdditive().isEmpty()) {
				referencedAdditives.add(currentAdditive);
				continue;
			}

			if (!currentAdditive.getFoodadditivesForAdditive().isEmpty()) {
				referencedAdditives.add(currentAdditive);
				continue;
			}

			if (!currentAdditive.getFoodbatchadditivesForAdditive().isEmpty()) {
				referencedAdditives.add(currentAdditive);
				continue;
			}

			if (!currentAdditive.getPurchaseadditivesForAdditive().isEmpty()) {
				referencedAdditives.add(currentAdditive);
				continue;
			}
		}
		return referencedAdditives;
	}

	public Set<Additive> loadAdditivesByIdSet(Set<Integer> additiveIdSet) {

		Iterator<Integer> additiveIdIterator = additiveIdSet.iterator();
		Set<Additive> additiveSet = new HashSet<>();
		while (additiveIdIterator.hasNext()) {
			Integer id = additiveIdIterator.next();
			Additive currentAdditive = additiveDAO.findAdditiveById(id);
			additiveSet.add(currentAdditive);
		}
		return additiveSet;
	}

	@Transactional
	public List<Additive> findAllAdditives(Integer startResult, Integer maxRows) {
		return new java.util.ArrayList<Additive>(additiveDAO.findAllAdditives(startResult, maxRows));
	}

	@Transactional
	public Additive findAdditiveByPrimaryKey(Integer id) {
		return additiveDAO.findAdditiveByPrimaryKey(id);
	}

	@Transactional
	public Integer countAdditives() {
		return ((Long) additiveDAO.createQuerySingleResult("select count(o) from Additive o").getSingleResult())
				.intValue();
	}

	@Transactional
	public Integer countAdditives(CmiPagination page) {
		String sql = CmiSqlUtil.createCountHql(page, Additive.class);
		return ((Long) additiveDAO.createQuerySingleResult(sql).getSingleResult()).intValue();
	}

	/**
	 * search for name with ajax
	 * 
	 */
	@Transactional
	public Integer countAdditivesByName(String queryName) {
		Query query = additiveDAO.createNamedQuery("countAdditivesByName", -1, -1);
		query.setParameter(1, queryName);
		return new Long((long) query.getSingleResult()).intValue();
	}

	@Transactional
	public Integer countAdditivesByDetachedCriteria(CmiPagination page) {
		DetachedCriteria detachedCriteria = CmiSqlUtil.createDetachedCriteria(page, Additive.class);
		Session session = entityManager.unwrap(Session.class);
		detachedCriteria.setProjection(Projections.rowCount());
		return new Long((long) detachedCriteria.getExecutableCriteria(session).uniqueResult()).intValue();
	}

	public Map<String, ?> loadAdditiveObjectReferencedSet(Additive additive) {
		HashMap<String, Object> objectReferencedSetMap = new HashMap<>();

		Set<Integer> additiveaddresssIds = new HashSet<Integer>();
		for (Additiveaddress obj : additive.getAdditiveaddresssForAdditive()) {

			additiveaddresssIds.add(obj.getId());
		}
		objectReferencedSetMap.put("additiveaddresssIds", additiveaddresssIds);
		Set<Integer> foodadditivesIds = new HashSet<Integer>();
		for (Foodadditive obj : additive.getFoodadditivesForAdditive()) {

			foodadditivesIds.add(obj.getId());
		}
		objectReferencedSetMap.put("foodadditivesIds", foodadditivesIds);
		Set<Integer> foodbatchadditivesIds = new HashSet<Integer>();
		for (Foodbatchadditive obj : additive.getFoodbatchadditivesForAdditive()) {

			foodbatchadditivesIds.add(obj.getId());
		}
		objectReferencedSetMap.put("foodbatchadditivesIds", foodbatchadditivesIds);
		Set<Integer> purchaseadditivesIds = new HashSet<Integer>();
		for (Purchaseadditive obj : additive.getPurchaseadditivesForAdditive()) {

			purchaseadditivesIds.add(obj.getId());
		}
		objectReferencedSetMap.put("purchaseadditivesIds", purchaseadditivesIds);

		return objectReferencedSetMap;
	}

	public Map<String, ?> loadAdditiveClassReferencedSet(Additive additive) {
		HashMap<String, Object> classReferencedSetMap = new HashMap<>();
		classReferencedSetMap.put("additiveaddresss", additiveaddressDAO.findAllAdditiveaddresss());
		classReferencedSetMap.put("foodadditives", foodadditiveDAO.findAllFoodadditives());
		classReferencedSetMap.put("foodbatchadditives", foodbatchadditiveDAO.findAllFoodbatchadditives());
		classReferencedSetMap.put("purchaseadditives", purchaseadditiveDAO.findAllPurchaseadditives());

		return classReferencedSetMap;
	}

	public boolean isEquals(Additive additive, Object obj) {
		if (obj == additive)
			return true;
		if (!(obj instanceof Additive))
			return false;
		Additive comparedAdditive = (Additive) obj;
		if (!CheckPropertyEqual(additive.getName(), comparedAdditive.getName())) {
			return false;
		}
		return true;
	}

	public static <T> boolean CheckPropertyEqual(T originProperty, T comparedProperty) {
		// 基本类型经过参数传递后会自动装箱
		String[] className = originProperty.getClass().getName().split("\\.");
		if (className[0].equals("java") && className[1].equals("math")) {
			return ((Comparable) originProperty).compareTo(comparedProperty) == 0 ? true : false;
		} else {
			return originProperty.equals(comparedProperty);
		}
	}
}

